﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using Ionic.Zip;
using Neo4jRestSharp.Neo4jObjectServices;
using NUnit.Framework;


namespace Neo4jRestSharp.Test
{
    [TestFixture]
    public class BaseServicesTest
    {
        protected readonly Uri neo4jRestUri = new Uri("http://localhost:9999/");
        private const string NEO4J_PATH = "\\neo4j-rest-standalone-0.8-SNAPSHOT";
        private const string NEO4J_BIN = "\\bin";
        private const string NEO4J_CONF = "\\conf";
        private const string NEO4J_LIB = "\\lib";        
        private const string NEO4J_DB = "\\neo4j-rest-db";
        private const string NEO4J_SVC = "\\neo4j-rest";

        private const string NEO4J_STOP = "stop";
        private const string NEO4J_REMOVE = "remove";
        private const string NEO4J_INSTALL = "install";
        private const string NEO4J_START = "start";
        
        public const string NODE1_NAME = "Morpheus";
        public const string NODE1_TITLE = "Holds the red pill";
        public const string NODE1_TITLE_2 = "Holds the blue pill";
        public const string NODE2_NAME = "Neo";
        public const string NODE2_TITLE = "Programmer by day";
        public const string NODE2_TITLE_2 = "The One";

        //Extract binaries for neo4j rest service
        private void ExtractZip(string zipFilename, string servicePath)
        {            
            using (ZipFile zip = new ZipFile(zipFilename))
            {
                zip.ExtractAll(servicePath);
            }            
        }

        private void CleanUpZipFolders(string servicePath)
        {
            //blow away the bin folder
            if (Directory.Exists(servicePath + NEO4J_BIN))
            {
                System.Threading.Thread.Sleep(500);
                Directory.Delete(servicePath + NEO4J_BIN, true);
                System.Threading.Thread.Sleep(500);
            }
            //blow away the config folder
            if (Directory.Exists(servicePath + NEO4J_CONF))
            {
                System.Threading.Thread.Sleep(500);
                Directory.Delete(servicePath + NEO4J_CONF, true);
                System.Threading.Thread.Sleep(500);
            }
            //blow away the lucene data
            if (Directory.Exists(servicePath + NEO4J_DB))
            {
                System.Threading.Thread.Sleep(500);
                Directory.Delete(servicePath + NEO4J_DB, true);
                System.Threading.Thread.Sleep(500);
            }
            //blow away the lib folder
            if (Directory.Exists(servicePath + NEO4J_LIB))
            {
                System.Threading.Thread.Sleep(500);
                Directory.Delete(servicePath + NEO4J_LIB, true);
                System.Threading.Thread.Sleep(500);
            }
        }

        [TestFixtureSetUp]
        public void SetupFixture()
        {
            string servicePath = AppDomain.CurrentDomain.BaseDirectory + NEO4J_PATH;
           
            Process proc = null;
            var services = ServiceController.GetServices();
            var neoService = services.Where(s => s.DisplayName == "Neo4j REST Server").FirstOrDefault();
            if (neoService != null)
            {
                if (neoService.Status == ServiceControllerStatus.Running)
                {
                    //stop the rest service
                    proc = Process.Start(servicePath + NEO4J_BIN + NEO4J_SVC, NEO4J_STOP);
                    proc.WaitForExit();
                }

                //remove the rest service
                proc = Process.Start(servicePath + NEO4J_BIN + NEO4J_SVC, NEO4J_REMOVE);
                proc.WaitForExit();
            }

            //extract zip file with the neo4j binaries
            CleanUpZipFolders(servicePath);
            ExtractZip(servicePath + "\\neo4jRest.zip", servicePath);

            //install the rest service
            proc = Process.Start(servicePath + NEO4J_BIN + NEO4J_SVC, NEO4J_INSTALL);
            proc.WaitForExit();
            //start the rest service
            proc = Process.Start(servicePath + NEO4J_BIN + NEO4J_SVC, NEO4J_START);
            proc.WaitForExit();
            System.Threading.Thread.Sleep(500);
        }

        [TestFixtureTearDown]
        public void SetupTearDown()
        {
            string servicePath = AppDomain.CurrentDomain.BaseDirectory + NEO4J_PATH;

            //stop the rest service
            Process proc = Process.Start(servicePath + NEO4J_BIN + NEO4J_SVC, NEO4J_STOP);
            proc.WaitForExit();
            //remove the rest service
            proc = Process.Start(servicePath + NEO4J_BIN + NEO4J_SVC, NEO4J_REMOVE);
            proc.WaitForExit();

            CleanUpZipFolders(servicePath);
        }

		public T CreatePerson<T>(string name, string title) where T : Person, new()
		{
			INodeObjectServices nodeObjectServices = ServicesFactory.ObjectServices(neo4jRestUri);
			//GraphDatabaseService gds = ServicesFactory.GraphDatabaseServices(neo4jRestUri);

			//create empty node of type T
			T person = nodeObjectServices.CreateNode<T>();			

			//set name and title
			person.Name = name;
			person.Title = title;			

			//if matrix person, set values
			MatrixPerson mp = person as MatrixPerson;
			if (mp != null)
			{
				mp.IsAgent = false;
				mp.IsHuman = true;
			}

			//save properties
			person.SaveProperties();

			//get node for saved person
			T returnPerson = nodeObjectServices.GetNode<T>(person.Id);			

			//double check values
			Assert.IsNotNull(returnPerson);
			Assert.AreEqual(person.Id, returnPerson.Id);
			Assert.AreEqual(person.Name, returnPerson.Name);
			Assert.AreEqual(person.Title, returnPerson.Title);
			MatrixPerson mp2 = returnPerson as MatrixPerson;
			if (mp != null && mp2 != null)
			{
				Assert.AreEqual(mp.IsHuman, mp2.IsHuman);
				Assert.AreEqual(mp.IsAgent, mp2.IsAgent);
			}

			//return the sucker
			return returnPerson;
		}
    }
}
